grammar Lucenedb;

options
{
output=AST;
ASTLabelType=CommonTree;
backtrack=false;
k=3;
}

@header {
package com.googlecode.lucenedb.ql.antlr;
import  com.googlecode.lucenedb.ql.entity.*;
import  com.googlecode.lucenedb.ql.entity.condition.*;
}


@lexer::header {
   package com.googlecode.lucenedb.ql.antlr;
   import  com.googlecode.lucenedb.ql.entity.*;
   import  com.googlecode.lucenedb.ql.entity.condition.*;
}

@members {
	  private SelectBody selectBody = null;
      public SelectBody getSelectBody(){
         return this.selectBody;
      }
}

@lexer::members
{
    public void reportError(RecognitionException e) 
    {
        StringBuilder errorMessage = new StringBuilder("Syntax error at position " + e.charPositionInLine + ": ");
        if (e instanceof NoViableAltException)
        {
            int index = e.charPositionInLine;
            String error = this.input.substring(index, index);
            String statement = this.input.substring(0, this.input.size() - 1);

            errorMessage.append("unexpected \"" + error + "\" for `" + statement + "`.");
        }
        else
        {
            errorMessage.append(this.getErrorMessage(e, this.getTokenNames()));
        }

        throw new RuntimeException(errorMessage.toString());
    }
}









@rulecatch {
catch (RecognitionException ex) {
 reportError(ex);
  throw ex;
}
}





stemtent:
          selectStemtent EOF;///| deleteStemtent | updaetStemtent;


selectStemtent
@init{
         selectBody = new SelectBody();
      }
      :KW_SELECT  (ASTERISK|selectList[selectBody])  KW_FROM  tabName = identifier { selectBody.setTableName($tabName.text);}  
      sqlCondition[selectBody]?  
      groupby[selectBody]? 
      orderby[selectBody]?   
      limit[selectBody]?;


selectList[SelectBody selectBody]
@init{
         List<Column> columns = new ArrayList<Column>();
         selectBody.setColumns(columns);
         int i = 0;
       }
       :  selectItem[columns,i++] (COMMA selectItem[columns,i++])*
       ;
       
       
	
selectItem[List<Column> columns,int index]
   @init { 
      Column column = new Column(); 
      column.setIndex(columns.size());
      columns.add(column);
   }
    :	
     columnItem[column] (KW_AS*   aliasName=identifier  {column.setAliasName($aliasName.text);} )*
     
     {
	           
     }
     ;
	
	
columnItem [Column column]
:    colName1=identifier 
	      {
	           column.setName($colName1.text);
	           column.setFieldName($colName1.text);
	      }
	
	|   funText = functionColumnItem[column]
	    {
	       column.setName($funText.text);
	    }
	;
	
functionColumnItem[Column column]
 :	
	funName=identifier LPAREN args0=text { column.addArgs($args0.text); column.setFieldName($args0.text);} 
	(COMMA args=text { column.addArgs($args.text); } )*    RPAREN
	{
	    column.setFuntionName($funName.text);
	}
	
;
	
	
	
	
orderby[SelectBody selectBody]
@init{
        OrderByExpr expr = new OrderByExpr();
        selectBody.setOrderByExpr(expr);
    }
    :	
      'ORDER BY'   orderStr[expr]  (COMMA orderStr[expr] )*;
      
      
orderStr[OrderByExpr expr]
    @init{
        Order order = new Order();
        expr.addValue(order);
    }	
:
  v1=identifier { order.setName($v1.text);}  ('@'  v3=identifier { order.setDataType($v3.text);} )*  ( v2=identifier { order.setSort($v2.text);} )*
;


groupby[SelectBody selectBody]
   @init{
          GroupByExpr expr = new GroupByExpr();
          selectBody.setGroupByExpr(expr);
   }
   :	
  'GROUP BY' v1=identifier { expr.setFieldName($v1.text); }   ('@' sort= groupSort  {expr.setSort($sort.text);} )*
;

groupSort:  'INDEXORDER'|'RELEVANCE' ;
     


limit[SelectBody selectBody]	
@init{
          LimitExpr expr = new LimitExpr();
          selectBody.setLimitExpr(expr);
   }
   :	
     'LIMIT'  v1=N {expr.setStart(Integer.parseInt($v1.text));}  (COMMA v2=N {expr.setEnd(Integer.parseInt($v2.text));})*

;


identifier
	:	ID;


//deleteStemtent
//	:	;

//updaetStemtent
//	:	;
	
	
sqlCondition[SelectBody selectBody]
	:
	{
	   ConditionGroup defConditionGroup  = new ConditionGroup();
	   defConditionGroup.setCondition(true);
	   selectBody.setConditionGroup(defConditionGroup);
	}
	KW_WHERE condition_or[defConditionGroup,true]
	;


condition_or[ConditionGroup conditionGroup,boolean condition]
	: ca0=condition_and[conditionGroup,condition] ( 'OR' can=condition_and[conditionGroup,false] )*
	;

condition_and[ConditionGroup conditionGroup,boolean condition]
	:condition_PAREN[conditionGroup,condition] ( 'AND' condition_PAREN[conditionGroup,true] )*
	;


condition_PAREN[ConditionGroup conditionGroup,boolean condition]
	:
	 {
	   ConditionGroup childConditionGroup  = new ConditionGroup();
	   childConditionGroup.setCondition(condition);
	   childConditionGroup.setIndex(conditionGroup.getChildConditionGroups().size());
	   conditionGroup.getChildConditionGroups().add(childConditionGroup);
	 }
	 LPAREN condition_or[childConditionGroup,true] RPAREN
	|  {
	       ConditionExpr expr = new ConditionExpr(); 
	       expr.setCondition(condition);
	       expr.setIndex(conditionGroup.getChildConditionGroups().size());
	       conditionGroup.getChildConditionGroups().add(expr);
	   } 
	   condition_expr[expr]
	;
	
	

condition_expr [ConditionExpr expr]
       
	: 
	 nanme=condition_left  condition_right[expr]
	  {
	    expr.setName($nanme.text);
	  }
	;
	
condition_right [ConditionExpr expr]
        @init{
            ConditionRight  conditionRight = null;
        }
	:	{conditionRight = new CompareCondition(); expr.setConditionRight(conditionRight);}  comp_expr[conditionRight] 
	 |      {conditionRight = new InCondition(); expr.setConditionRight(conditionRight);}  in_expr[conditionRight]
	 |      {conditionRight = new LikeCondition(); expr.setConditionRight(conditionRight);}  like_expr[conditionRight]
	 |      {conditionRight = new BetweenCondition(); expr.setConditionRight(conditionRight);}  between_expr[conditionRight]
	 ;
	
comp_expr  [ConditionRight conditionRight]
 : op=relational_op  val=value
	  {
	     
	     ((CompareCondition)conditionRight).setCop($op.text);
	     ((CompareCondition)conditionRight).setValue($val.text);
	  
	  }
	;
	

value	
	:N
	|NUMBER
	|quoted_string
	;
	
text 	:( N*  NUMBER*  ID*) | quoted_string;
	
	
 in_expr[ConditionRight conditionRight]	
 :     
    not='NOT'?  {
                  if($not!=null){
                       ((InCondition)conditionRight).setNot(true);
                  }else{
                       ((InCondition)conditionRight).setNot(false);
                  }
    
                }   
    'IN'  LPAREN v1=value{((InCondition)conditionRight).addValue($v1.text);} (COMMA v2=value{((InCondition)conditionRight).addValue($v2.text);})*  RPAREN;
    
    
 like_expr[ConditionRight conditionRight]
 	:
 	not='NOT'?  {
                  if($not!=null){
                       ((LikeCondition)conditionRight).setNot(true);
                  }else{
                       ((LikeCondition)conditionRight).setNot(false);
                  }
    
        }
        
        'LIKE' 	val=quoted_string{((LikeCondition)conditionRight).setValue($val.text);}
 	
 	;
 	
 	
 between_expr[ConditionRight conditionRight]:
      not='NOT'?  {
                  if($not!=null){
                       ((BetweenCondition)conditionRight).setNot(true);
                  }else{
                       ((BetweenCondition)conditionRight).setNot(false);
                  }
    
        }
        
        'BETWEEN' LPAREN s=(N|NUMBER) {((BetweenCondition)conditionRight).setStart($s.text);}  COMMA e=(N|NUMBER){((BetweenCondition)conditionRight).setEnd($e.text);} RPAREN
        
        ;
 
	
	
relational_op
	:	EQ | LTH | GTH | NOT_EQ | LEQ | GEQ
	;
	
condition_left
	:	identifier ;	



quoted_string
	: QUOTED_STRING
	;
//params_text
//	:	PARAMS;	
	

QUOTED_STRING
	:'\'' (~'\''|'\'\''|'\\\'')* '\''
	;

//PARAMS
//	:	(~ COMMA|'\''COMMA)*;

KW_AS : 'AS';

KW_SELECT : 'SELECT';

KW_FROM : 'FROM';

KW_ORDER : 'ORDER';

KW_WHERE: 'WHERE';

KW_GROUP : 'GROUP';

KW_BY : 'BY';

KW_LIMIT : 'LIMIT';


COMMA : ',' ;

RPAREN
	:	')'
	;
LPAREN
	:	'('
	;

//identifier
   // :
   //  (Letter | Digit | '_' | '-' )+
  //  ;

	
WS  :   (   ' '
		|   '\t'
		|   '\r' '\n' 
		|   '\n'     
		|   '\r'      
		)
		{$channel=HIDDEN;}  //ignore this token
	;


fragment
Letter
    : 'a'..'z' | 'A'..'Z'
    ;

fragment
Digit
    :
        '0'..'9'
    ;

PLUS
	:	'+'
	;

MINUS
	:	'-'
	;
	
DIVIDE
	:	'/'
	;
	
	
ASTERISK
	:	'*'
	
	;

N        : '0' .. '9' ( '0' .. '9' )*
	;
	
NUMBER
	:	
	(( N '.' N )
	|('.' N)
	)
    ;
    
ID 
    :	('A' .. 'Z'|'a'..'z') ( 'A' .. 'Z'|'a'..'z' | '0' .. '9' | '_' | '$' | '#' )*
    ;


EQ
	:	'='
	;
	
LTH
	:	'<'
	;

NOT_EQ
	:	'<>' | '!=' | '^='
	;
LEQ
	:	'<='
	;

GEQ
	:	'>='
	;

GTH
	:	'>'
	;
	
SQOUT	:	'\''
;



